// tspcluster - Traveling Salesman Problem
// Copyright (C) 2006  Frederik Carlier
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

using System;
using System.IO;
using System.Globalization;
using System.Threading;
using System.ServiceProcess;
using System.Diagnostics;
using System.Collections.ObjectModel;

namespace UGent.Tsp
{
    class ProfileSwappers : ServiceBase
    {
        public ProfileSwappers()
        {
            this.ServiceName = "ProfileSwappers";
        }

        protected override void OnStart(string[] args)
        {
            running = true;
            thread = new Thread(new ThreadStart(Run));
            thread.Start();
        }

        protected override void OnStop()
        {
            running = false;
            thread.Join();
        }

        // The number of sweeps to run to get the information
        // about the variables d1, d2 and d3
        private const int Precision = 20;
        private const int BetaMax = 20;
        private const int StepsPerChain = 5000000;
        private const int CountUpper = 98;
        private const int CountLower = 2;
        private const int CountRange = CountUpper - CountLower + 1;
        private bool running = false;
        private bool optimumOnly = true;
        private Thread thread;

        public void Run()
        {
            if (!running)
                running = true;

            double beta;
            int time;
            int swapperCount;
            int measurementCount;
            int measurementIndex;

            Results[] average;
            Results[] deviation;
            Results[] autocorrelationTime;
            Results[] acceptance;
            Results[] steps;
            Results[] times;
            string[] names;
            double[] betas;
            int[] counts;

            /*************************************************
             * Do a Markov simulation for all values of beta *
             *************************************************/

            MarkovCluster cluster = new MarkovCluster();

            // Get the list of nodes
            cluster.ConfigureOnConsole();
            ReadOnlyCollection<Type> swappers = Swappers.ListSwappers();

            swapperCount = 0;
            foreach (Type swapperType in swappers)
            {
                if( !Swappers.HasParameter(swapperType) )
                {
                    Console.WriteLine("Swapper {0}, {1} counts", swapperType, 1);
                    swapperCount += 1;
                }
                else if (!optimumOnly)
                {
                    Console.WriteLine("Swapper {0}, {1} counts", swapperType, CountRange);
                    swapperCount += CountRange;
                }
                else
                {
                    Console.WriteLine("Swapper {0}, {1} counts", swapperType, Swappers.GetOptimalValues(swapperType).Count);
                    swapperCount += Swappers.GetOptimalValues(swapperType).Count;
                }
            }

            measurementCount = swapperCount * (Precision + 1);

            Console.WriteLine("Total number of swappers: {0}", swapperCount);
            Console.WriteLine("Total number of measurements: {0}", measurementCount);

            average = new Results[measurementCount];
            deviation = new Results[measurementCount];
            autocorrelationTime = new Results[measurementCount];
            acceptance = new Results[measurementCount];
            steps = new Results[measurementCount];
            times = new Results[measurementCount];
            names = new string[measurementCount];
            betas = new double[measurementCount];
            counts = new int[measurementCount];

            while (running)
            {
                measurementIndex = 0;
                Map map = new Map(100);
                cluster.SetMap(map);

                for (int step = 0; step <= Precision; step++)
                {
                    beta = (double)step / (double)Precision * BetaMax;
                    cluster.SetBeta(beta);

                    foreach (Type swapperType in swappers)
                    {
                        Collection<int> range = new Collection<int>();
                        if(!Swappers.HasParameter(swapperType))
                            range.Add(1);
                        else if(optimumOnly)
                        {
                            ReadOnlyCollection<int> optimumValues = Swappers.GetOptimalValues(swapperType);
                            foreach(int count in optimumValues)
                                range.Add(count);
                        }
                        else
                            for (int count = CountLower; count <= CountUpper; count++)
                                range.Add(count);

                        Console.WriteLine("Number of items in range: {0}", range.Count);

                        foreach (int count in range)
                        {
                            names[measurementIndex] = string.Format("{0}", swapperType);
                            betas[measurementIndex] = beta;
                            counts[measurementIndex] = count;

                            Console.Write("Sampling swapper {0} for n = {1} and beta = {2}", swapperType, count, beta);

                            if (map == null)
                                Console.WriteLine("Map is null!");
                            cluster.SetSwapper(swapperType, count);
                            cluster.Start();

                            time = 0;

                            while (cluster.Progress < StepsPerChain)
                            {
                                Thread.Sleep(MarkovCluster.Interval);
                                Console.Write('.');
                                time++;
                            }

                            Console.WriteLine();

                            cluster.Stop();
                            cluster.WaitForAll();

                            Results results = cluster.GetResults();
                            if (average[measurementIndex] == null)
                                average[measurementIndex] = new Results();
                            average[measurementIndex].Add(results.GetAverage());

                            if (deviation[measurementIndex] == null)
                                deviation[measurementIndex] = new Results();
                            deviation[measurementIndex].Add(results.GetDeviation());

                            if (autocorrelationTime[measurementIndex] == null)
                                autocorrelationTime[measurementIndex] = new Results();
                            autocorrelationTime[measurementIndex].Add(cluster.EstimateAutocorrelationTime());

                            if (acceptance[measurementIndex] == null)
                                acceptance[measurementIndex] = new Results();
                            acceptance[measurementIndex].Add(results.AcceptanceRate);

                            if (steps[measurementIndex] == null)
                                steps[measurementIndex] = new Results();
                            steps[measurementIndex].Add(results.Count);

                            if (times[measurementIndex] == null)
                                times[measurementIndex] = new Results();
                            times[measurementIndex].Add(time * MarkovCluster.Interval);

                            Console.WriteLine("Average energy: {0}", results.GetAverage());
                            Console.WriteLine("Deviation: {0}", results.GetDeviation());
                            Console.WriteLine("Autocorrelation time: {0}", cluster.EstimateAutocorrelationTime());
                            Console.WriteLine("Acceptance: {0}", results.AcceptanceRate);
                            Console.WriteLine("Steps: {0}", results.Count);
                            Console.WriteLine("Time (s): {0}", time * MarkovCluster.Interval);

                            measurementIndex++;
                        }

                        if (!running)
                            break;
                    }

                    using (FileStream stream = File.OpenWrite("profiler.txt"))
                    using (StreamWriter writer = new StreamWriter(stream))
                    {
                        for (int i = 0; i < measurementIndex; i++)
                        {
                            if (average[i] == null)
                                continue;

                            writer.Write(string.Format(CultureInfo.InvariantCulture, "{0}, ", i));
                            writer.Write(string.Format(CultureInfo.InvariantCulture, "{0}, ", betas[i]));
                            writer.Write(string.Format(CultureInfo.InvariantCulture, "{0}, ", names[i]));
                            writer.Write(string.Format(CultureInfo.InvariantCulture, "{0}, ", counts[i]));
                            writer.Write(string.Format(CultureInfo.InvariantCulture, "{0}, ", average[i].GetAverage()));
                            writer.Write(string.Format(CultureInfo.InvariantCulture, "{0}, ", average[i].GetDeviation()));
                            writer.Write(string.Format(CultureInfo.InvariantCulture, "{0}, ", deviation[i].GetAverage()));
                            writer.Write(string.Format(CultureInfo.InvariantCulture, "{0}, ", deviation[i].GetDeviation()));
                            writer.Write(string.Format(CultureInfo.InvariantCulture, "{0}, ", autocorrelationTime[i].GetAverage()));
                            writer.Write(string.Format(CultureInfo.InvariantCulture, "{0}, ", autocorrelationTime[i].GetDeviation()));
                            writer.Write(string.Format(CultureInfo.InvariantCulture, "{0}, ", acceptance[i].GetAverage()));
                            writer.Write(string.Format(CultureInfo.InvariantCulture, "{0}, ", acceptance[i].GetDeviation()));
                            writer.Write(string.Format(CultureInfo.InvariantCulture, "{0}, ", steps[i].GetAverage()));
                            writer.Write(string.Format(CultureInfo.InvariantCulture, "{0}, ", steps[i].GetDeviation()));
                            writer.Write(string.Format(CultureInfo.InvariantCulture, "{0}, ", times[i].GetAverage()));
                            writer.Write(string.Format(CultureInfo.InvariantCulture, "{0}, ", times[i].GetDeviation()));
                            writer.WriteLine();
                            writer.Flush();
                        }
                    }

                    if (!running)
                        break;
                }
            }
        }

        public static void RunOnConsole()
        {
            ProfileSwappers profiler = new ProfileSwappers();
            profiler.Run();
        }
    }
}
